/*
 * Copyright (c) 2023 fred
 * Licensed under the Apache License,Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
import { Reader } from './Reader';
import hilog from '@ohos.hilog';


export class StringReader extends Reader {
  private _str:string
  private _length:number
  private _next:number = 0
  private _mark:number = 0


  /**
   * Creates a new string reader.
   *
   * @param s  String providing the character stream.
   */
  constructor(str:string) {
    super();
    hilog.info(0x0000, 'testTag', '%{public}s', `load aaaaa1112222`);
    this._str = str
    this._length = str.length
    hilog.info(0x0000, 'testTag', '%{public}s', `load aaaaa111111`);
  }

  /** Check to make sure that the stream has not been closed */
  private ensureOpen() {
    if (this._str === undefined || this._str === null) {
      throw new Error("Stream closed");
    }
  }

  /**
   * Reads a single character.
   *
   * @return     The character read, or -1 if the end of the stream has been
   *             reached
   *
   * @exception  IOException  If an I/O error occurs
   */
  public read():number {
    this.ensureOpen()
    if (this._next >= this._length) {
      return -1
    }
    return this._str.charCodeAt(this._next++)
  }

  /**
   * Reads characters into a portion of an array.
   *
   * @param      cbuf  Destination buffer
   * @param      off   Offset at which to start writing characters
   * @param      len   Maximum number of characters to read
   *
   * @return     The number of characters read, or -1 if the end of the
   *             stream has been reached
   *
   * @exception  IOException  If an I/O error occurs
   * @exception  IndexOutOfBoundsException {@inheritDoc}
   */
  public read1(cbuf: string[],cBufLen:number,off:number, len:number):number {
    this.ensureOpen()
    if (off < 0 || off > cBufLen || len < 0 || off + len > cBufLen || off + len < 0) {
      throw new Error(`Index out of range:${off} or ${len}`);
    } else if (len === 0) {
      return 0
    }
    if (this._next >= this._length) {
      return -1
    }
    let n = Math.min(this._length - this._next, len)
    cbuf.splice(off, n, ...this._str.slice(this._next, this._next + n));
    this._next += n;
    return n;
  }

  /**
   * Skips the specified number of characters in the stream. Returns
   * the number of characters that were skipped.
   *
   * <p>The <code>ns</code> parameter may be negative, even though the
   * <code>skip</code> method of the {@link Reader} superclass throws
   * an exception in this case. Negative values of <code>ns</code> cause the
   * stream to skip backwards. Negative return values indicate a skip
   * backwards. It is not possible to skip backwards past the beginning of
   * the string.
   *
   * <p>If the entire string has been read or skipped, then this method has
   * no effect and always returns 0.
   *
   * @exception  IOException  If an I/O error occurs
   */
  public skip(ns:number):number {
    this.ensureOpen()
    if (this._next >= this._length) {
      return 0
    }
    let n = Math.min(this._length - this._next, ns)
    n = Math.max(-this._next,n)
    this._next += n
    return n
  }

  /**
   * Tells whether this stream is ready to be read.
   *
   * @return True if the next read() is guaranteed not to block for input
   *
   * @exception  IOException  If the stream is closed
   */
  public ready():boolean {
    this.ensureOpen()
    return true
  }

  /**
   * Tells whether this stream supports the mark() operation, which it does.
   */
  public markSupported():boolean {
    return true
  }

  /**
   * Marks the present position in the stream.  Subsequent calls to reset()
   * will reposition the stream to this point.
   *
   * @param  readAheadLimit  Limit on the number of characters that may be
   *                         read while still preserving the mark.  Because
   *                         the stream's input comes from a string, there
   *                         is no actual limit, so this argument must not
   *                         be negative, but is otherwise ignored.
   *
   * @exception  IllegalArgumentException  If {@code readAheadLimit < 0}
   * @exception  IOException  If an I/O error occurs
   */
  public mark(readAheadLimit:number){
    if (readAheadLimit < 0) {
      throw new Error("Read-ahead limit < 0");
    }
    this.ensureOpen()
    this._mark = this._next
  }

  /**
   * Resets the stream to the most recent mark, or to the beginning of the
   * string if it has never been marked.
   *
   * @exception  IOException  If an I/O error occurs
   */
  public reset() {
    this.ensureOpen()
    this._next = this._mark
  }

  /**
   * Closes the stream and releases any system resources associated with
   * it. Once the stream has been closed, further read(),
   * ready(), mark(), or reset() invocations will throw an IOException.
   * Closing a previously closed stream has no effect. This method will block
   * while there is another thread blocking on the reader.
   */
  public close() {
    this._str = null
  }


}
